﻿using System.IO;
using System;
using System.Linq;
using Args;
using Args.Help;
using Cumulux.BootStrapper;
using Cumulux.BootStrapper.Contracts;

namespace BootStrapper
{
    internal class Program
    {
        private static IArgumentsParser parser = new AzureArgumentsParser();

        internal static void Main(string[] args)
        {
            try
            {
                // Expand first and then bind to Args
                var expanded = args.Select(a => parser.ParseArguments(a)).ToArray();

                var config = Args.Configuration.Configure<CommandArgs>();

                var cmds = config.AsFluent()
                    .UsingStringComparer(StringComparer.InvariantCultureIgnoreCase)
                    .UsingSwitchDelimiter("/")
                    .Initialize()
                    .CreateAndBind(expanded);

                if (args.Length < 1 || args.Length == 1 && cmds.ShowHelp)
                {
                    var help = new HelpProvider();
                    var commandsHelp = help.GenerateModelHelp<CommandArgs>(config);

                    var formatter = new Args.Help.Formatters.ConsoleHelpFormatter();
                    formatter.WriteHelp(commandsHelp, Console.Out);

                    return;
                }

                // TODO: Research for a better way to validate parameters with Args
                try
                {
                    ValidateArgs(cmds);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    Console.WriteLine();
                    Console.WriteLine("Try bootstrapper -help for more information");
                }

                //simple mapping
                var bootArgs = new BootStrapperArgs
                    {
                        Args = cmds.Args,
                        Block = cmds.Block,
                        EnableSystemProfile = cmds.EnableSystemProfile,
                        Get = cmds.Get,
                        LocalResource = cmds.LocalResource,
                        Overwrite = cmds.Overwrite,
                        Put = cmds.Put,
                        Run = cmds.Run,
                        RunAlways = cmds.RunAlways,
                        StorageConnection = cmds.StorageConnection,
                        Unzip = !String.IsNullOrWhiteSpace(cmds.Unzip),
                        UnzipTarget = cmds.Unzip
                    };

                //default to trace logger
                var logger = new TraceLogger();
                var manager = new BootStrapperManager(logger, new PackageDownloader(logger), new PackageRunner(logger),
                                                      new PackageUnzipper(logger), new PackageUploader(logger),
                                                      new PackageZipper(logger));

                manager.Start(bootArgs);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());

                // TODO: Push error to blob storage?
            }
        }

        private static void ValidateArgs(CommandArgs command)
        {
            if ((!String.IsNullOrEmpty(command.Get) && String.IsNullOrEmpty(command.Unzip)) || !String.IsNullOrEmpty(command.Put))
            {
                if (String.IsNullOrEmpty(command.LocalResource))
                {
                    throw new ArgumentException("LocalResource is required.");
                }

                try
                {
                    new FileInfo(command.LocalResource);
                }
                catch (ArgumentException ae)
                {
                    throw new ArgumentException("Invalid local resource path.", ae);
                }
                catch (PathTooLongException pl)
                {
                    throw new PathTooLongException("Local resource exceeds allowable path length.", pl);
                }
                catch (NotSupportedException ns)
                {
                    throw new ArgumentException("The local resource path not supported.", ns);
                }
            }

            if (String.IsNullOrEmpty(command.StorageConnection))
            {
                if (!String.IsNullOrEmpty(command.Get) && !Uri.IsWellFormedUriString(command.Get, UriKind.Absolute))
                {
                    throw new ArgumentException(
                        "A valid connection string to Azure blob storage must be supplied when downloading from a relative URL.",
                        command.Get);
                }

                if (!String.IsNullOrEmpty(command.Put) && !Uri.IsWellFormedUriString(command.Put, UriKind.Absolute))
                {
                    throw new ArgumentException(
                        "A valid connection string to Azure blob storage must be supplied when uploading to a relative URL.",
                        command.Get);
                }
            }
        }
    }
}
